package org.dftproject.genesis.ui.editors;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import org.dftproject.genesis.ui.Activator;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.IStatusLineManager;
import org.eclipse.jface.action.ToolBarManager;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.browser.LocationListener;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.browser.StatusTextEvent;
import org.eclipse.swt.browser.StatusTextListener;
import org.eclipse.swt.browser.TitleEvent;
import org.eclipse.swt.browser.TitleListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.PartInitException;

public class BrowserEditor extends EditorPartEx {

	public static final String ID = BrowserEditor.class.getName();
	public static final String BROWSER_CONTENT_PROVIDERS_EXTENSION_POINT = "browserContentProviders";

	private Image backImage;
	private Image forwardImage;
	private Image refreshImage;
	private Image stopImage;

	private ToolItem backBtn;
	private ToolItem forwardBtn;
	private Text addressBar;
	private Browser browser;
	
	private final BrowserSelectionProvider selectionProvider = new BrowserSelectionProvider();

	@Override
	public void doSave(IProgressMonitor monitor) {
	}

	@Override
	public void doSaveAs() {
	}

	@Override
	public void init(IEditorSite site, IEditorInput input) throws PartInitException {
		if (!(input instanceof BrowserEditorInput))
			throw new PartInitException("input must be a " + BrowserEditorInput.class.getName());
		this.setSite(site);
		this.setInput(input);
		site.setSelectionProvider(selectionProvider);
	}

	@Override
	public boolean isDirty() {
		return false;
	}

	@Override
	public boolean isSaveAsAllowed() {
		return false;
	}

	@Override
	public void createControls(Composite parent) {
		GridLayout layout = new GridLayout();
		layout.numColumns = 1;
		layout.verticalSpacing = 0;
		layout.marginTop = layout.marginRight = layout.marginBottom = layout.marginLeft = layout.marginWidth = layout.marginHeight = 0;
		parent.setLayout(layout);

		Composite navigationBar = new Composite(parent, SWT.NONE);
		navigationBar.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
		layout = new GridLayout();
		layout.numColumns = 3;
		layout.horizontalSpacing = 4;
		layout.marginTop = layout.marginRight = layout.marginBottom = layout.marginLeft = 4;
		layout.marginWidth = layout.marginHeight = 0;
		navigationBar.setLayout(layout);

		ToolBar navigationToolbar = new ToolBarManager(SWT.FLAT | SWT.RIGHT).createControl(navigationBar);
		navigationToolbar.setBackground(parent.getBackground());
		navigationToolbar.setForeground(parent.getForeground());

		backImage = Activator.getImageDescriptor("icons/resultset_previous.png").createImage();
		forwardImage = Activator.getImageDescriptor("icons/resultset_next.png").createImage();

		backBtn = new ToolItem(navigationToolbar, SWT.PUSH);
		backBtn.setImage(backImage);
		backBtn.setToolTipText("Back");
		backBtn.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				browser.back();
			}

		});

		forwardBtn = new ToolItem(navigationToolbar, SWT.PUSH);
		forwardBtn.setImage(forwardImage);
		forwardBtn.setToolTipText("Forward");
		forwardBtn.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				browser.forward();
			}

		});

		addressBar = new Text(navigationBar, SWT.BORDER);
		addressBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
		addressBar.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				browser.setUrl(addressBar.getText());
			}

		});

		ToolBar addressToolbar = new ToolBarManager(SWT.FLAT | SWT.RIGHT).createControl(navigationBar);
		addressToolbar.setBackground(parent.getBackground());
		addressToolbar.setForeground(parent.getForeground());

		refreshImage = Activator.getImageDescriptor("icons/arrow_refresh.png").createImage();
		stopImage = Activator.getImageDescriptor("icons/cancel.png").createImage();

		ToolItem refreshBtn = new ToolItem(addressToolbar, SWT.PUSH);
		refreshBtn.setImage(refreshImage);
		refreshBtn.setToolTipText("Refresh");
		refreshBtn.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				browser.refresh();
			}

		});

		ToolItem stopBtn = new ToolItem(addressToolbar, SWT.PUSH);
		stopBtn.setImage(stopImage);
		stopBtn.setToolTipText("Stop");
		stopBtn.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				browser.stop();
			}

		});

		browser = new Browser(parent, SWT.BORDER);
		browser.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
		browser.addStatusTextListener(new StatusTextListener() {

			public void changed(StatusTextEvent event) {
				IStatusLineManager status = getEditorSite().getActionBars().getStatusLineManager();
				status.setMessage(event.text.replaceAll("&", "&&"));

			}

		});
		browser.addProgressListener(new ProgressListener() {

			protected int progressWorked = 0;

			public void changed(ProgressEvent event) {
				if (event.total == 0)
					return;

				boolean done = (event.current == event.total);

				int percentProgress = event.current * 100 / event.total;

				IProgressMonitor monitor = getEditorSite().getActionBars().getStatusLineManager().getProgressMonitor();
				if (done) {
					monitor.done();
					progressWorked = 0;
				} else if (progressWorked == 0) {
					monitor.beginTask("", event.total);
					progressWorked = percentProgress;
				} else {
					monitor.worked(event.current - progressWorked);
					progressWorked = event.current;
				}

				updateControls();
			}

			public void completed(ProgressEvent event) {		
				IProgressMonitor monitor = getEditorSite().getActionBars().getStatusLineManager().getProgressMonitor();
				monitor.done();

				updateControls();
			}

		});
		browser.addLocationListener(new LocationListener() {

			public void changed(LocationEvent event) {
				if (!event.top)
					return;
					
				setTitleImage(Activator.getImageDescriptor("icons/page_white_world.png").createImage());
				addressBar.setText(event.location);
				selectionProvider.changed(event.location);
				
				setInput(new BrowserEditorInput(event.location));
			}

			public void changing(LocationEvent event) {
			}

		});
		browser.addTitleListener(new TitleListener() {

			public void changed(TitleEvent event) {
				if (!event.title.equals(getPartName())) {
					setTitleImage(Activator.getImageDescriptor("icons/page_white_world.png").createImage());
					setPartName(event.title);
				}
			}

		});

		updateControls();

		IEditorInput input = getEditorInput();
		if (input instanceof BrowserEditorInput) {
			setUri(((BrowserEditorInput) input).getUri());
		} else {
			setUri("about:blank");
		}
	}

	@Override
	public void setFocus() {
		browser.setFocus();
	}

	@Override
	public void dispose() {
		stopImage.dispose();
		refreshImage.dispose();
		forwardImage.dispose();
		backImage.dispose();
		super.dispose();
	}

	protected void updateControls() {
		backBtn.setEnabled(browser.isBackEnabled());
		forwardBtn.setEnabled(browser.isForwardEnabled());
	}
	
	protected void setUri(String uri) {
		browser.setUrl(uri);
	}
	
	protected class BrowserSelectionProvider implements ISelectionProvider {
			
		private final Set<ISelectionChangedListener> listeners = Collections.synchronizedSet(new HashSet<ISelectionChangedListener>());
		private ISelection selection;

		public void addSelectionChangedListener(ISelectionChangedListener listener) {
			listeners.add(listener);
		}

		public ISelection getSelection() {
			return selection;
		}

		public void removeSelectionChangedListener(ISelectionChangedListener listener) {
			listeners.add(listener);
		}

		public void setSelection(ISelection selection) {
			if (this.selection == selection)
				return;
			this.selection = selection;
			
			synchronized (listeners) {
				if (!listeners.isEmpty()) {
					SelectionChangedEvent event = new SelectionChangedEvent(this, selection);
					
					Iterator<ISelectionChangedListener> iter = listeners.iterator();
					while (iter.hasNext())
						iter.next().selectionChanged(event);
				}
			}
		}
		
		public void changed(String url) {
			try {
				setSelection(new StructuredSelection(new URI(url)));
			} catch (URISyntaxException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
			
	}

}
